﻿using System;
using Baula.TextGen.Framework.Sections;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Baula.TextGen.Framework.Tests
{
    [TestClass]
    public class DocumentTests
    {

        [TestMethod]
        public void RenderToString_WithEmptyTemplate_GeneratesEmptyOutput()
        {
            var documentTemplate = new DocumentTemplate(string.Empty);

            var document = documentTemplate.CreateDocument();
            var actualContent = document.RenderToString();

            Assert.AreEqual(string.Empty, actualContent);
        }

        [TestMethod]
        public void RenderToString_WithLiteralTemplate_ReturnsThatTemplate()
        {
            const string templateContent = @"Option Explicit

Public Sub Class_Initialize()
'
End Sub

Public Sub Class_Terminate()
'
End Sub";
            var documentTemplate = new DocumentTemplate(templateContent);

            var document = documentTemplate.CreateDocument();
            var actualContent = document.RenderToString();

            Assert.AreEqual(templateContent, actualContent);
        }

        [TestMethod]
        public void RenderToString_WithSinglePlaceholderTemplate_ReplacesThePlaceholder()
        {
            const string templateText = @"This is a literal text
and here is some %Info% to be replaced.
Here some finishing text again.";
            const string info = "really important info";
            const string resultingText = @"This is a literal text
and here is some really important info to be replaced.
Here some finishing text again.";

            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();
            document.ValuesForPlaceholders["Info"] = info;
            var actualContent = document.RenderToString();

            Assert.AreEqual(resultingText, actualContent);
        }

        [TestMethod]
        [ExpectedException(typeof(MissingPlaceholderValueException))]
        public void RenderToString_WithSinglePlaceholderNoValueGiven_ThrowsMissingPlaceholderValueException()
        {
            const string templateText = @"This is a literal text
and here is some %Info% to be replaced.
Here some finishing text again.";

            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();
            //document.ValuesForPlaceholders["Info"] = info;   <-- not done!
            document.RenderToString();
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ValuesForPlaceholder_SettingValueOfNonExistingPlaceholder_ThrowsException()
        {
            const string templateText = "Here is %Placeholder1%, and here %Placeholder2%.";
            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();
            document.ValuesForPlaceholders["PlaceholderThatDoesNotExist"] = "unimportant value";
        }

        [TestMethod]
        public void RenderToString_WithLiteralTemplateWithSinglePart_ReturnsEverythingButThePart()
        {
            const string templateText =
                @"Here is the beginning of the file
with some useless things to forget

' === MySinglePart
But this is already part of the part named MySinglePart
";
            const string expectedContent =
                @"Here is the beginning of the file
with some useless things to forget
";
            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();
            var actualContent = document.RenderToString();

            Assert.AreEqual(expectedContent, actualContent);
        }

        [TestMethod]
        public void RenderToString_WithSinglePartOnePlaceholderAndOneRelocationAddingPartOnce_RendersRelocationInsteadOfPlaceholder()
        {
            const string templateText = @"' %Section:Here%
' === A part
' @Section:Here
This text shall be relocated";
            const string expectedText = @"' %Section:Here%
This text shall be relocated";

            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();
            var partTemplate = documentTemplate.PartTemplates["A part"];
            var part = new Part(partTemplate);
            document.Parts.Add(part);

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithSinglePartOnePlaceholderAndOneRelocationAddingPartTwice_RendersRelocationInsteadOfPlaceholderTwice()
        {
            const string templateText = @"' %Section:Here%
' === A part
' @Section:Here
This text shall be relocated";
            const string expectedText = @"' %Section:Here%
This text shall be relocated
This text shall be relocated";

            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();
            var partTemplate = documentTemplate.PartTemplates["A part"];
            var part = new Part(partTemplate);
            document.Parts.Add(part);
            document.Parts.Add(part);

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithSinglePartOnePlaceholderAndTwoRelocationsAddingPartOnce_RendersRelocationsInsteadOfPlaceholder()
        {
            const string templateText = @"' %Section:Here%
' === A part
' @Section:Here
This text shall be relocated
' @Section:Here
This text shall be relocated as well";
            const string expectedText = @"' %Section:Here%
This text shall be relocated
This text shall be relocated as well";

            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();
            var partTemplate = documentTemplate.PartTemplates["A part"];
            var part = new Part(partTemplate);
            document.Parts.Add(part);

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithSinglePartTwoPlaceholdersAndTwoRelocationsAddingPartOnce_RendersRelocationsInsteadOfPlaceholders()
        {
            const string templateText = @"' %Section:FirstPlace%
' %Section:SecondPlace%
' === A part
' @Section:FirstPlace
This text shall be relocated to the first place
' @Section:SecondPlace
This text shall be relocated to the second place";
            const string expectedText = @"' %Section:FirstPlace%
This text shall be relocated to the first place
' %Section:SecondPlace%
This text shall be relocated to the second place";

            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();
            var partTemplate = documentTemplate.PartTemplates["A part"];
            var part = partTemplate.CreatePart();
            document.Parts.Add(part);

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithPlaceholderInRelocationSection_RendersWithReplacedPlaceholders()
        {
            const string templateText =
                @"' %Section:PlaceToMoveTo%
' === The Part
' @Section:PlaceToMoveTo
This shall be %replaced%!";
            const string expectedText =
                @"' %Section:PlaceToMoveTo%
This shall be a great tool!";

            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();
            var partTemplate = documentTemplate.PartTemplates["The Part"];
            var part = partTemplate.CreatePart();
            part.ValuesForPlaceholders["replaced"] = "a great tool";
            document.Parts.Add(part);

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithIntendedPlaceholderSection_RendersPlaceholderSectionWithIntendation()
        {
            const string templateText = "   ' %Section:IntentedSection%";

            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();

            var actualText = document.RenderToString();

            Assert.AreEqual(templateText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithContentSectionStartingWithNewLines_KeepsThatNewLines()
        {
            const string templateText = @"

The first line was blank";
            const string expectedText = templateText;

            var documentTemplate = new DocumentTemplate(templateText);
            var document = documentTemplate.CreateDocument();

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithTwoPartTemplatesAddingPartsInSameOrder_RendersWithoutExcessNewLines()
        {
            const string templateText = @"' === PartA
Content of PartA
' === PartB
Content of PartB";
            const string expectedText = @"Content of PartA
Content of PartB";

            var documentTemplate = new DocumentTemplate(templateText);
            var partATemplate = documentTemplate.PartTemplates["PartA"];
            var partBTemplate = documentTemplate.PartTemplates["PartB"];

            var document = documentTemplate.CreateDocument();
            document.Parts.Add(partATemplate.CreatePart());
            document.Parts.Add(partBTemplate.CreatePart());

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithTwoPartTemplatesAddingPartsInOppositeOrder_RendersInOrderOfThePartsNotTheTemplates()
        {
            const string templateText = @"' === PartA
Content of PartA
' === PartB
Content of PartB";
            const string expectedText = @"Content of PartB
Content of PartA";

            var documentTemplate = new DocumentTemplate(templateText);
            var partATemplate = documentTemplate.PartTemplates["PartA"];
            var partBTemplate = documentTemplate.PartTemplates["PartB"];

            var document = documentTemplate.CreateDocument();
            document.Parts.Add(partBTemplate.CreatePart());
            document.Parts.Add(partATemplate.CreatePart());

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }
        
        [TestMethod]
        public void RenderToString_WithRelocationToEnd_AppendsToTheEnd()
        {
            const string templateContent =
                @"' %Section:UserdefinedSection%
' === A Part
This shall be appended to the end before.
' @Section:UserdefinedSection
Am I in the UserdefinedSection now?
' @Section:End
And this shall be appended to the end afterwards.";
            const string expectedText =
                @"' %Section:UserdefinedSection%
Am I in the UserdefinedSection now?
This shall be appended to the end before.
And this shall be appended to the end afterwards.";

            var documentTemplate = new DocumentTemplate(templateContent);
            var partTemplate = documentTemplate.PartTemplates["A Part"];

            var document = documentTemplate.CreateDocument();
            document.Parts.Add(partTemplate.CreatePart());

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithRelocationToEndEmbeddedInOtherPart_AppendsAtLocationOfThePart()
        {
            const string templateContent =
                @"' %Section:UserdefinedSection%
' === Part with End relocation
This shall be appended to the end before.
' @Section:UserdefinedSection
Am I in the UserdefinedSection now?
' @Section:End
And this shall be appended to the end afterwards.
' === Normal part
That's from the normal part";
            const string expectedText =
                @"' %Section:UserdefinedSection%
Am I in the UserdefinedSection now?
That's from the normal part
This shall be appended to the end before.
And this shall be appended to the end afterwards.
That's from the normal part";

            var documentTemplate = new DocumentTemplate(templateContent);
            var partTemplateWithEndRelocation = documentTemplate.PartTemplates["Part with End relocation"];
            var partTemplateNormal = documentTemplate.PartTemplates["Normal part"];

            var document = documentTemplate.CreateDocument();
            document.Parts.Add(partTemplateNormal.CreatePart());
            document.Parts.Add(partTemplateWithEndRelocation.CreatePart());
            document.Parts.Add(partTemplateNormal.CreatePart());

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        public void RenderToString_WithPartWithRelocationToUserdefinedSectionAndToEndAndOtherPartWithRelocation_PutsAllPartsWhereTheyBelong()
        {
            const string templateContent =
                @"' %Section:UserdefinedSection%
' === Part with End relocation
This shall be appended to the end before.
' @Section:UserdefinedSection
Am I in the UserdefinedSection now?
' @Section:End
And this shall be appended to the end afterwards.
' === Normal part
That's from the normal part
' @Section:UserdefinedSection
That comes from the normal part to the UserdefinedSection";
            const string expectedText =
                @"' %Section:UserdefinedSection%
That comes from the normal part to the UserdefinedSection
Am I in the UserdefinedSection now?
That comes from the normal part to the UserdefinedSection
That's from the normal part
This shall be appended to the end before.
And this shall be appended to the end afterwards.
That's from the normal part";

            var documentTemplate = new DocumentTemplate(templateContent);
            var partTemplateWithEndRelocation = documentTemplate.PartTemplates["Part with End relocation"];
            var partTemplateNormal = documentTemplate.PartTemplates["Normal part"];

            var document = documentTemplate.CreateDocument();
            document.Parts.Add(partTemplateNormal.CreatePart());
            document.Parts.Add(partTemplateWithEndRelocation.CreatePart());
            document.Parts.Add(partTemplateNormal.CreatePart());

            var actualText = document.RenderToString();

            Assert.AreEqual(expectedText, actualText);
        }

        [TestMethod]
        [ExpectedException(typeof(UnknownPlaceholderSectionException))]
        public void RenderToString_RelocationSectionReferringToNonexistingPlaceholderSection_ThrowsProperException()
        {
            const string templateContent = @"' %Section:ExistingSection1%
Content of ExistingSection1.
' %Section:ExistingSection2%
Content of ExistingSection2.
' === A test part
' @Section:SectionThatDoesNotExist";

            var documentTemplate = new DocumentTemplate(templateContent);
            var partTemplate = documentTemplate.PartTemplates["A test part"];

            var document = documentTemplate.CreateDocument();
            var part = partTemplate.CreatePart();
            document.Parts.Add(part);

            document.RenderToString();  // The interesting thing happens here
        }
    }
}
